//
//  Carl Uzarski, Brendan Murray
//  Copyright (c) 2014 SoCIS. All rights reserved.
//

#include "Polynom.h"
#include <limits>
#include <cmath>
#include <stdlib.h>
using namespace std;


namespace gvsu {

ostream& operator<< (ostream& os, const Polynom& poly){
	if (poly.coef_list.empty()){
		return os;
	}
	os << poly[0].first << "x^" << poly[0].second;
	for (unsigned int i = 1; i < poly.coef_list.size();i++){
		os << " ";
		if (poly[i].first > 0){
			os << "+ " << poly[i].first << "x^" << poly[i].second;
		}
		else{
			os << "- " << abs(poly[i].first) << "x^" << poly[i].second;
		}
	}
	return os;
}


Polynom::Polynom ()
{
    coef_list = {};
    expo_list = {};
}

Polynom::Polynom (initializer_list<float> C, initializer_list<int> E)
{
    /* the following for-loops show how to work with std::initializer_list */
    for (auto iter = C.begin(); iter !=  C.end(); ++iter) {
        coef_list.push_back(*iter);
    }
    
    for (int expo : E) {
        expo_list.push_back(expo);
    }
}

Polynom::Polynom(float co, int ex){
	coef_list = {};
	expo_list = {};
	coef_list.push_back(co);
	expo_list.push_back(ex);
}

int Polynom::number_of_terms() const{
	return this->coef_list.size();
}

Polynom Polynom::operator+ (const Polynom& other) const{
	if (this->coef_list.empty() && this->expo_list.empty()){
		return other;
	}
	if (other.coef_list.empty() && other.expo_list.empty()){
		return *this;
	}
	vector<float> c;
	vector<int> e;
	Polynom answer;
	unsigned int otherdex = 0;
	for (unsigned int thisdex = 0;thisdex < expo_list.size(); thisdex++){
		// int equaldex = otherdex; //equals otherdex except when expos differ
		if(otherdex < other.expo_list.size()){
			for(;other.expo_list[otherdex] > this->expo_list[thisdex]; otherdex++){
				e.push_back(other.expo_list[otherdex]);
				c.push_back(other.coef_list[otherdex]);
			}
			if(other.expo_list[otherdex] == this->expo_list[thisdex]){
				if(other.coef_list[otherdex] + this->coef_list[thisdex] != 0){
					e.push_back(other.expo_list[otherdex]);
					c.push_back(other.coef_list[otherdex] + this->coef_list[thisdex]);
				}
				otherdex++;
			}
			else /*(this->expo_list[thisdex] > other.expo_list[otherdex] )*/{
				e.push_back(this->expo_list[thisdex]);
				c.push_back(this->coef_list[thisdex]);
			}
		}
		else{
			e.push_back(this->expo_list[thisdex]);
			c.push_back(this->coef_list[thisdex]);
		}
		}

	answer.coef_list = c;
	answer.expo_list = e;
	return answer;
}

Polynom& Polynom::operator += (const Polynom& p){
	*this = (*this + p);
	return *this;
}
    
pair<float,int> Polynom::operator[] (int index) const{
	pair<float, int> answer;
	answer.first = this->coef_list[index];
	answer.second = this->expo_list[index];
	return answer;
}

Polynom Polynom::operator* (float m) const{
	vector<float> c;
	Polynom answer;
	if (m != 0){
		answer.expo_list = this->expo_list;
		for (auto iter = this->coef_list.begin(); iter != coef_list.end(); ++iter) {
			c.push_back(m * *iter);
		}
		answer.coef_list = c;
	}
	return answer;
}

Polynom operator* (float val, const Polynom& p){
	return p*val;
}

Polynom Polynom::operator- (const Polynom& other) const{
	Polynom answer;
	answer = *this + (other * (-1));
	return answer;
}

Polynom& Polynom::operator *= (float m){
	*this = (*this * m);
	return *this;
}

double Polynom::operator() (double val) const{
	double answer = 0.0;
	for (unsigned int i = 0; i < this->expo_list.size(); i++){
		answer += (pow(val, expo_list[i])) * coef_list[i];
	}
	return answer;
}

Polynom Polynom::operator* (const Polynom& other) const{
	Polynom answer;
//	if (!(this->expo_list.empty()) && !(other.expo_list.empty())) {
//
//	}
	for (unsigned int i = 0; i < this->expo_list.size(); ++i){
		for (unsigned int j = 0; j < other.expo_list.size(); ++j){
			float c = this->coef_list[i] * other.coef_list[j];
			int e = this->expo_list[i] + other.expo_list[j];
			Polynom temp (c,e);
			answer = answer + temp;
		}
	}
	return answer;
}


}
